
namespace ObjectPersistence {
    using System;
    using System.Data.Common;
    using System.Collections.Generic;
    using BusinessLayerHelper;
    
    public abstract class AssemblerBase<PersistentType, CriteriaType, RowMapType, DomainObjectFactoryType> : IDisposable
        where PersistentType :  class
        where CriteriaType : CriteriaBase
        where RowMapType : RowMapBase, new ()
        where DomainObjectFactoryType : DomainObjectFactoryBase<PersistentType, RowMapType>, new () {

        /// <summary>Reads a single (the first result found) Object from the DBMS from a specified WHERE clause. If no results are found, then null will be returned</summary>
        /// <param name="Where">The WHERE clause.  Don't put the word WHERE, just the columns and values.</param>
        public PersistentType ReadSingle(string Where) {
            return this.readSingleAndClose(this.issueQuery(this.GetWhere(Where)));
        }
        /// <summary>Reads a single (the first result found) Object from the DBMS from a specified WHERE and ORDER BY clause. If no results are found, then null will be returned</summary>
        /// <param name="Where">The WHERE clause.  Don't put the word WHERE, just the columns and values.</param>
        /// <param name="OrderBy">The ORDER BY clause.  Don't put the words ORDER BY, just the columns and values.</param>
        public PersistentType ReadSingle(string Where, string OrderBy) {
            return this.readSingleAndClose(this.issueQuery(this.GetWhereOrderBy(Where, OrderBy)));
        }

        /// <summary>Reads a single object from the database corresponding to the conditions set forth in the criteria object. If this criteria object corresponds to multiple objects in the database, the first will be returned, and subsequent ones will be ignored</summary>
        /// <param name="criteria">criteria object which specifies relational conditions which must be satisfied by the DBMS</param>
        public PersistentType ReadSingle(CriteriaType criteria) {
            return this.readSingleAndClose(this.issueQuery(criteria));
        }

        /// <summary>Reads all Objects from the DBMS which satisfy the conditions set forth in the Criteria object. If this criteria object corresponds to no objects in the database, then an empty collection will be returned</summary>
        /// <param name="criteria">Criteria object which specifies relational conditions which must be satisfied by the DBMS</param>
        public BusinessLayerHelper.PersistentList<PersistentType> ReadCollection(CriteriaType criteria) {
            return ObjectPersistenceHelper.fillCollectionAndClose<PersistentType>(this.fillCollectionHelper(this.issueQuery(criteria)));
        }
        /// <summary>Reads all Objects from the DBMS from a specified WHERE clause. If no results are found, then an empty collection will be returned</summary>
        /// <param name="Where">The WHERE clause.  Don't put the word WHERE, just the columns and values.</param>
        public BusinessLayerHelper.PersistentList<PersistentType> ReadCollection(string Where) {
            return ObjectPersistenceHelper.fillCollectionAndClose<PersistentType>(this.fillCollectionHelper(this.issueQuery(this.GetWhere(Where))));
        }
        /// <summary>Reads all Objects from the DBMS from a specified WHERE and ORDER BY clause. If no results are found, then an empty collection will be returned</summary>
        /// <param name="Where">The WHERE clause.  Don't put the word WHERE, just the columns and values.</param>
        /// <param name="OrderBy">The ORDER BY clause.  Don't put the words ORDER BY, just the columns.</param>
        public BusinessLayerHelper.PersistentList<PersistentType> ReadCollection(string Where, string OrderBy) {
            return ObjectPersistenceHelper.fillCollectionAndClose<PersistentType>(this.fillCollectionHelper(this.issueQuery(this.GetWhereOrderBy(Where, OrderBy))));
        }
        /// <summary>Reads all Objects from the DBMS</summary>
        public BusinessLayerHelper.PersistentList<PersistentType> ReadCollectionAll() {
            return ObjectPersistenceHelper.fillCollectionAndClose<PersistentType>(this.fillCollectionHelper(this.issueQuery("")));
        }
        /// <summary>Reads all Objects from the DBMS</summary>
        /// <param name="OrderBy">Specified the SQL ORDER BY Expression.  Do not type the words ORDER BY, just the columns</param>
        public BusinessLayerHelper.PersistentList<PersistentType> ReadCollectionAll(string OrderBy) {
            return ObjectPersistenceHelper.fillCollectionAndClose<PersistentType>(this.fillCollectionHelper(this.issueQuery(this.GetOrderBy(OrderBy))));
        }

        protected System.Data.Common.DbDataReader issueQuery(string sql) {
            return this.runQuery((this.Abstract_SELECT 
                            + (" FROM " 
                            + (this.InstanceTable 
                            + (" " + sql)))));
        }
        protected System.Data.Common.DbDataReader issueQueryRaw(string sql) {
            return this.runQuery(sql);
        }
        protected System.Data.Common.DbDataReader runQuery(string sql) {
            this.connection.Open();
            System.Data.Common.DbCommand comm = this.connection.CreateCommand();
            comm.CommandText = sql;
            return comm.ExecuteReader();
        }
        protected System.Data.Common.DbDataReader issueQuery(CriteriaType criteria) {
            this.connection.Open();
            System.Data.Common.DbCommand comm = this.connection.CreateCommand();
            comm.CommandText = this.SelectFromTable;
            SelectionBase<CriteriaType> _tempSB = new SelectionBase<CriteriaType>();
            _tempSB.getSelection(criteria, comm);
            return comm.ExecuteReader();
        }
        protected PersistentType readSingleAndClose(System.Data.Common.DbDataReader dbRows) {
            if ((ObjectPersistenceHelper.checkSingleRead(dbRows, this.InstanceConnection) == false)) {
                return null;
            }
            RowMapType row = this.fillRowMap(dbRows);
            dbRows.Close();
            InstanceConnection.Close();
            DomainObjectFactoryType _tempDOF = new DomainObjectFactoryType();
            return _tempDOF.newDomainObject(row);
        }

        protected List<PersistentType> fillCollectionHelper(System.Data.Common.DbDataReader dbRows) {
            List<PersistentType> result = new List<PersistentType>();
            DomainObjectFactoryType dof = new DomainObjectFactoryType();
            RowMapType row = new RowMapType();
            for (int ignore = 0; dbRows.Read(); ignore = (ignore + 1)) {
                row = this.fillRowMap(dbRows);
                result.Add(dof.newDomainObject(row));
            }
            dbRows.Close();
            connection.Close();
            return result;
        }
        internal BusinessLayerHelper.PersistentList<PersistentType> ReadCollectionCustom(System.Data.Common.DbCommand comm) {
            return ObjectPersistenceHelper.fillCollectionAndClose<PersistentType>(this.fillCollectionHelper(comm.ExecuteReader()));
        }

        protected abstract RowMapType fillRowMap(System.Data.Common.DbDataReader reader);

        internal abstract void ClearList();

        protected abstract string InstanceTable {
            get;
        }

        protected abstract System.Data.Common.DbConnection InstanceConnection {
            get;
        }

        public abstract string Abstract_SELECT {
            get;
        }

        protected string SelectFromTable {
            get {
                return (this.Abstract_SELECT + this.GetFrom());
            }
        }

        protected string GetWhereOrderBy(string Where, string OrderBy) {
            return (this.GetWhere(Where) + this.GetOrderBy(OrderBy));
        }

        protected string GetFrom() {
            return (" FROM " + this.InstanceTable);
        }

        protected string GetWhere(string Where) {
            return (" WHERE " + Where);
        }

        protected string GetOrderBy(string OrderBy) {
            return (" ORDER BY " + OrderBy);
        }

        protected System.Data.Common.DbConnection connection;

        public void Dispose() {
            connection.Dispose();
        }
    }
    public abstract class AssemblerBaseUpdatable<PersistentType, CriteriaType, RowMapType, DomainObjectFactoryType> : AssemblerBase<PersistentType, CriteriaType, RowMapType, DomainObjectFactoryType>
        where PersistentType :  class
        where CriteriaType : CriteriaBase
        where RowMapType : RowMapBase, new ()
        where DomainObjectFactoryType : DomainObjectFactoryBase<PersistentType, RowMapType>, new () {
        /// <summary>Persists the given Object to the DBMS, inserting it if it is new, and updating it otherwise.  If a true value is passed to the cascade parameters then all child objects will be recursively persisted as well</summary>
        /// <param name="PersistentObject">The Object to be persisted</param>
        /// <param name="cascade">Specifies whether or not to recursively persist child objects and collections</param>
        public void MakePersistent(PersistentType PersistentObject, bool cascade) {
            this.MakePersistent(PersistentObject, cascade, true);
        }
        /// <summary>Persists the given Object to the DBMS, inserting it if it is new, and updating it otherwise.  If a true value is passed to the cascade parameters then all child objects will be recursively persisted as well</summary>
        /// <param name="PersistentObject">The Object to be persisted</param>
        /// <param name="cascade">Specifies whether or not to recursively persist child objects and collections</param>
        /// <param name="Transac">Specifies whether or not to use transaction management during the operation.  If this operation involves more than one database then a TransactionScope will be created.  Otherwise, a System.Data.Common.DbTransaction will be used</param>
        public void MakePersistent(PersistentType PersistentObject, bool cascade, bool Transac) {
            this.ClearList();

            if (Transac) {
                this.MakePersistent(PersistentObject, cascade, GlobalUtil.Constants.TransactionType.Simple);
            }
            else {
                this.connection.Open();
                this.MakePersistent(PersistentObject, cascade, this.connection.CreateCommand());
                this.connection.Close();
            }
        }
        /// <summary>Persists the given Object to the DBMS, inserting it if it is new, and updating it otherwise.  If a true value is passed to the cascade parameters then all child objects will be recursively persisted as well</summary>
        /// <param name="PersistentObject">The Object to be persisted</param>
        /// <param name="cascade">Specifies whether or not to recursively persist child objects and collections</param>
        /// <param name="TranType">Specifies the type of transaction management to use. If TransactionType.Simple is specified then a System.Data.Common.DbTransaction will be created. If TransactionType.Distributed is specified then a TransactionScope will be created.</param>
        public void MakePersistent(PersistentType PersistentObject, bool cascade, GlobalUtil.Constants.TransactionType TranType) {
            this.ClearList();

            if ((TranType == GlobalUtil.Constants.TransactionType.Simple)) {
                this.connection.Open();
                System.Data.Common.DbCommand comm = this.connection.CreateCommand();
                System.Data.Common.DbTransaction t = this.connection.BeginTransaction();
                comm.Transaction = t;
                try {
                    this.MakePersistent(PersistentObject, cascade, comm);
                    t.Commit();
                }
                catch (System.Exception e) {
                    t.Rollback();
                    this.HandleDBException(e);
                }
                finally {
                    this.connection.Close();
                }
            }
            else {
                System.Transactions.TransactionScope ts = new System.Transactions.TransactionScope();
                System.Data.Common.DbConnection conn = this.InstanceConnection;
                System.Data.Common.DbCommand comm = connection.CreateCommand();
                try {
                    conn.Open();
                    this.MakePersistent(PersistentObject, cascade, comm);
                    ts.Complete();
                }
                catch (System.Exception e) {
                    this.HandleDBException(e);
                }
                finally {
                    conn.Close();
                    ts.Dispose();
                }
            }
        }

        internal abstract void MakePersistent(PersistentType PersistentObject, bool cascade, System.Data.Common.DbCommand comm);

        /// <summary>Deletes the given Object from the DBMS.</summary>
        /// <param name="PersistentObject">The Object to be deleted</param>
        public void Delete(PersistentType PersistentObject) {
            this.Delete(PersistentObject, true);
        }

        /// <summary>Deletes the given Object from the DBMS.</summary>
        /// <param name="PersistentObject">The Object to be deleted</param>
        /// <param name="Transac">Specifies whether or not to use transaction management.</param>
        public void Delete(PersistentType PersistentObject, bool Transac) {
            this.ClearList();

            if (Transac) {
                this.connection.Open();
                System.Data.Common.DbCommand comm = this.connection.CreateCommand();
                System.Data.Common.DbTransaction t = this.connection.BeginTransaction();
                comm.Transaction = t;
                try {
                    this.Delete(PersistentObject, comm);
                    t.Commit();
                }
                catch (System.Exception e) {
                    t.Rollback();
                    this.HandleDBException(e);
                }
                finally {
                    this.connection.Close();
                }
            }
            else {
                System.Data.Common.DbConnection conn = this.InstanceConnection;
                try {
                    conn.Open();
                    this.Delete(PersistentObject, conn.CreateCommand());
                }
                catch (System.Exception e) {
                    this.HandleDBException(e);
                }
                finally {
                    conn.Close();
                }
            }
        }

        internal abstract void Delete(PersistentType PersistentObject, System.Data.Common.DbCommand comm);

        private void HandleDBException(System.Exception e) {
            if ((typeof(NoRowsUpdated) == e.GetType())) {
                throw new NoRowsUpdated((GlobalUtil.Constants.PersistException + e.Message), e);
            }
            if ((typeof(OptimisticLockException) == e.GetType())) {
                throw new OptimisticLockException((GlobalUtil.Constants.PersistException + e.Message), e);
            }
            throw new System.Exception((GlobalUtil.Constants.PersistException + e.Message), e);
        }
    }
    public class SelectionBase<CriteriaType>
        where CriteriaType : CriteriaBase {

        internal void getSelection(CriteriaType c, System.Data.Common.DbCommand comm) {
            bool first = true;
            int counter = 0;
            System.Collections.Generic.IEnumerator<System.Collections.Generic.KeyValuePair<string, object>> Pairs;
            System.Collections.Generic.IEnumerator<System.Collections.Generic.KeyValuePair<string, System.Collections.IEnumerable>> INPairs;

            Pairs = c.EqualsCollection.GetEnumerator();
            for (int ignore1 = 0; Pairs.MoveNext(); ignore1 = (ignore1 + 1)) {
                this.addParam(comm, "=", ref first, Pairs.Current.Key, ("@paramValue" + counter), Pairs.Current.Value);
                counter = (counter + 1);
            }
            Pairs = c.NotEqualsCollection.GetEnumerator();
            for (int ignore2 = 0; Pairs.MoveNext(); ignore2 = (ignore2 + 1)) {
                this.addParam(comm, "<>", ref first, Pairs.Current.Key, ("@paramValue" + counter), Pairs.Current.Value);
                counter = (counter + 1);
            }
            Pairs = c.LessThanCollection.GetEnumerator();
            for (int ignore3 = 0; Pairs.MoveNext(); ignore3 = (ignore3 + 1)) {
                this.addParam(comm, "<", ref first, Pairs.Current.Key, ("@paramValue" + counter), Pairs.Current.Value);
                counter = (counter + 1);
            }
            Pairs = c.GreaterThanCollection.GetEnumerator();
            for (int ignore4 = 0; Pairs.MoveNext(); ignore4 = (ignore4 + 1)) {
                this.addParam(comm, ">", ref first, Pairs.Current.Key, ("@paramValue" + counter), Pairs.Current.Value);
                counter = (counter + 1);
            }
            Pairs = c.LikeCollection.GetEnumerator();
            for (int ignore5 = 0; Pairs.MoveNext(); ignore5 = (ignore5 + 1)) {
                this.addParam(comm, "LIKE", ref first, Pairs.Current.Key, ("@paramValue" + counter), Pairs.Current.Value);
                counter = (counter + 1);
            }
            INPairs = c.INCollection.GetEnumerator();
            for (int ignore6 = 0; INPairs.MoveNext(); ignore6 = (ignore6 + 1)) {
                this.addParam(comm, "IN", ref first, INPairs.Current.Key, ("@paramValue" + counter), INPairs.Current.Value);
                counter = (counter + 1);
            }
            INPairs = c.NOTINCollection.GetEnumerator();
            for (int ignore7 = 0; INPairs.MoveNext(); ignore7 = (ignore7 + 1)) {
                this.addParam(comm, "NOT IN", ref first, INPairs.Current.Key, ("@paramValue" + counter), INPairs.Current.Value);
                counter = (counter + 1);
            }

            if (((c.SortColumns == "") 
                        == false)) {
                comm.CommandText = (comm.CommandText 
                            + ("ORDER BY " + c.SortColumns));
            }
        }
        protected void addParam(System.Data.Common.DbCommand comm, string OP, ref bool first, string prefix, string paramName, object o) {
            this.addInitialText(comm, ref first);
            if ((o == null)) {
                if ((OP == "=")) {
                    comm.CommandText = (comm.CommandText 
                                + (" " 
                                + (prefix + " IS NULL ")));
                }
                else {
                    comm.CommandText = (comm.CommandText 
                                + (" " 
                                + (prefix + " IS NOT NULL ")));
                }
            }
            else {
                comm.CommandText = (comm.CommandText 
                            + (" " 
                            + (prefix 
                            + (" " 
                            + (OP 
                            + (" " 
                            + (paramName + " ")))))));
                ObjectPersistenceHelper.AddParameterWithValue(comm, paramName, o);
            }
        }
        protected void addParam(System.Data.Common.DbCommand comm, string OP, ref bool first, string prefix, string paramName, System.Collections.IEnumerable o) {
            System.Collections.IEnumerator enumer = o.GetEnumerator();
            if ((enumer.MoveNext() == false)) {
                return;
            }
            enumer.Reset();

            this.addInitialText(comm, ref first);
            System.Text.StringBuilder vals = new System.Text.StringBuilder();

            vals.Append("(");
            for (int i = 0; enumer.MoveNext(); i = (i + 1)) {
                if ((i > 0)) {
                    vals.Append(",");
                }
                string paramID = ((paramName + OP.Replace(" ", "")) 
                            + i);
                vals.Append(paramID);
                ObjectPersistenceHelper.AddParameterWithValue(comm, paramID, enumer.Current);
            }

            vals.Append(") ");
            comm.CommandText = (comm.CommandText 
                        + (" " 
                        + (prefix 
                        + (" " 
                        + (OP 
                        + (" " + vals.ToString()))))));
        }
        protected void addInitialText(System.Data.Common.DbCommand comm, ref bool first) {
            if ((first == false)) {
                comm.CommandText = (comm.CommandText + " AND ");
            }
            else {
                first = false;
                comm.CommandText = (comm.CommandText + " WHERE ");
            }
        }
    }
    public class CriteriaBase {
        public enum AVAILABLE_OPERATORS {
            EQUALS,
            NOT_EQUALS,
            LESS_THAN,
            GREATER_THAN,
        }
        public enum AVAILABLE_STRING_OPERATORS {
            EQUALS,
            NOT_EQUALS,
            LESS_THAN,
            GREATER_THAN,
            LIKE,
        }
        public enum IN_NOT_IN {
            IN,
            NOT_IN,
        }

        internal System.Collections.Generic.Dictionary<string, object> EqualsCollection = new System.Collections.Generic.Dictionary<string, object>();
        internal System.Collections.Generic.Dictionary<string, object> NotEqualsCollection = new System.Collections.Generic.Dictionary<string, object>();
        internal System.Collections.Generic.Dictionary<string, object> LessThanCollection = new System.Collections.Generic.Dictionary<string, object>();
        internal System.Collections.Generic.Dictionary<string, object> GreaterThanCollection = new System.Collections.Generic.Dictionary<string, object>();
        internal System.Collections.Generic.Dictionary<string, object> LikeCollection = new System.Collections.Generic.Dictionary<string, object>();
        internal System.Collections.Generic.Dictionary<string, System.Collections.IEnumerable> INCollection = new System.Collections.Generic.Dictionary<string, System.Collections.IEnumerable>();
        internal System.Collections.Generic.Dictionary<string, System.Collections.IEnumerable> NOTINCollection = new System.Collections.Generic.Dictionary<string, System.Collections.IEnumerable>();

        protected void Clear() {
            EqualsCollection.Clear();
            NotEqualsCollection.Clear();
            LessThanCollection.Clear();
            GreaterThanCollection.Clear();
            LikeCollection.Clear();
            INCollection.Clear();
            NOTINCollection.Clear();
        }


        protected void setValue(IN_NOT_IN op, string key, System.Collections.IEnumerable value) {
            if ((op == IN_NOT_IN.IN)) {
                INCollection.Add(key, value);
            }
            else {
                NOTINCollection.Add(key, value);
            }
        }
        protected void setValue(AVAILABLE_OPERATORS op, string key, object value) {
            if (((op == AVAILABLE_OPERATORS.EQUALS) 
                        || (op == AVAILABLE_OPERATORS.NOT_EQUALS))) {
            }
            else {
                if ((value == null)) {
                    throw new System.InvalidOperationException("null value can only be set for EQUALS or NOT_EQUALS");
                }
            }

            if ((op == AVAILABLE_OPERATORS.EQUALS)) {
                EqualsCollection.Add(key, value);
            }
            if ((op == AVAILABLE_OPERATORS.NOT_EQUALS)) {
                NotEqualsCollection.Add(key, value);
            }
            if ((op == AVAILABLE_OPERATORS.LESS_THAN)) {
                LessThanCollection.Add(key, value);
            }
            if ((op == AVAILABLE_OPERATORS.GREATER_THAN)) {
                GreaterThanCollection.Add(key, value);
            }
        }
        protected void setValue(AVAILABLE_STRING_OPERATORS op, string key, object value) {
            if (((op == AVAILABLE_STRING_OPERATORS.EQUALS) 
                        || (op == AVAILABLE_STRING_OPERATORS.NOT_EQUALS))) {
            }
            else {
                if ((value == null)) {
                    throw new System.InvalidOperationException("null value can only be set for EQUALS or NOT_EQUALS");
                }
            }

            if ((op == AVAILABLE_STRING_OPERATORS.EQUALS)) {
                EqualsCollection.Add(key, value);
            }
            if ((op == AVAILABLE_STRING_OPERATORS.NOT_EQUALS)) {
                NotEqualsCollection.Add(key, value);
            }
            if ((op == AVAILABLE_STRING_OPERATORS.LESS_THAN)) {
                LessThanCollection.Add(key, value);
            }
            if ((op == AVAILABLE_STRING_OPERATORS.GREATER_THAN)) {
                GreaterThanCollection.Add(key, value);
            }
            if ((op == AVAILABLE_STRING_OPERATORS.LIKE)) {
                LikeCollection.Add(key, value);
            }
        }
        private string _sortExpression = "";
        public string SortColumns {
            get {
                return this._sortExpression;
            }
            set {
                this._sortExpression = value;
            }
        }
    }
    public abstract class RowMapBase {
    }
    public abstract class DomainObjectFactoryBase<PersistentType, RowMapType>
    
        where RowMapType : RowMapBase {

        public abstract PersistentType newDomainObject(RowMapType row);
    }
    public class NoRowsUpdated : System.Exception {
        public NoRowsUpdated(string message, System.Exception innerException) : 
                base(message, innerException) {
            this.addOn = message;
        }
        public NoRowsUpdated() {
        }

        private string addOn = "";
        public override string Message {
            get {
                return (this.addOn + "Warning, no rows were updated.  This may have been caused by an object key field " +
                    "being modified to one not existing in the database, or by the underlying databas" +
                    "e row being deleted outside of this application.");
            }
        }
    }
    public class OptimisticLockException : System.Exception {
        public OptimisticLockException(string message, System.Exception innerException) : 
                base(message, innerException) {
            this.addOn = message;
        }
        public OptimisticLockException() {
        }

        private string addOn = "";
        public override string Message {
            get {
                return (this.addOn + "Optimistic Lock Violation.  This means that in the time between when you read you" +
                    "r data from the database, and updated it, another user updated the same piece of" +
                    " information.  Please re-read the data in, and attempt your operation again:");
            }
        }
    }
    public class ObjectPersistenceHelper {
        public static bool checkSingleRead(System.Data.Common.DbDataReader dbRows, System.Data.Common.DbConnection connection) {
            if ((dbRows.Read() == false)) {
                dbRows.Close();
                connection.Close();
                return false;
            }
            return true;
        }
        public static BusinessLayerHelper.PersistentList<t> fillCollectionAndClose<t>(System.Collections.Generic.IList<t> initData)
         {
            BusinessLayerHelper.PersistentList<t> returnValue = new BusinessLayerHelper.PersistentList<t>();
            returnValue.FillData(initData);
            return returnValue;
        }
        public static void AddParameterWithValue(System.Data.Common.DbCommand comm, string name, object value) {
            ObjectPersistenceHelper.AddParameterWithValue(comm, name, value, null, null, null, null);
        }
        public static void AddParameterWithValue(System.Data.Common.DbCommand comm, string name, object value, System.Nullable<System.Data.SqlDbType> DataType, System.Nullable<int> Size, System.Nullable<byte> Precision, System.Nullable<byte> Scale) {
            System.Data.Common.DbParameter P = comm.CreateParameter();
            P.ParameterName = name;
            if ((value == null)) {
                P.Value = System.DBNull.Value;
            }
            else {
                P.Value = value;
            }
            if (((Size == null) 
                        == false)) {
                P.Size = Size.Value;
            }
            if (((Precision == null) 
                        == false)) {
                ((System.Data.SqlClient.SqlParameter)(P)).Precision = Precision.Value;
            }
            if (((Scale == null) 
                        == false)) {
                ((System.Data.SqlClient.SqlParameter)(P)).Scale = Scale.Value;
            }
            if (((DataType == null) 
                        == false)) {
                ((System.Data.SqlClient.SqlParameter)(P)).SqlDbType = DataType.Value;
            }
            comm.Parameters.Add(P);
        }
    }
}
namespace BusinessLayerHelper {
    using System.Collections.Generic;
    using System.Collections;
    
    public class PersistentList<PersistentObject> : object, IList<PersistentObject>, System.ComponentModel.IListSource,ICollection
     {
        // original set of objects, straight from the database
        private System.Collections.Generic.List<PersistentObject> originalList = new System.Collections.Generic.List<PersistentObject>();

        // //NEW items added to the collection
        public System.Collections.Generic.List<PersistentObject> Added = new System.Collections.Generic.List<PersistentObject>();

        // //items which were deleted
        public System.Collections.Generic.List<PersistentObject> Deleted = new System.Collections.Generic.List<PersistentObject>();

        internal System.Collections.Generic.List<PersistentObject> Master = new System.Collections.Generic.List<PersistentObject>();

        #region Helper Methods
        /// <summary>
        /// adds an item, removes, it, then re-adds it.  In this case the item would belong in the collection, but would also exist in
        /// the deleted collection.  It also clears items out of the added List which were originally present in the collection.
        /// This is for the case when the user swaps elements in the collection around.  Normally, when you write over an item, the
        /// old value gets added to the deleted List, and the new one to the added List.  When swapping, elements already in the list
        /// they should get added to neither.  This method clears out those entries of both types.
        /// </summary>
        internal void Synch() {
            Deleted.Clear();
            Added.Clear();
            originalList.Clear();
            originalList.AddRange(this.Master);
        }
        internal void Clean() {
            for (int i1 = 0; (i1 < Master.Count); i1 = (i1 + 1)) {
                if (Deleted.Contains(Master[i1])) {
                    Deleted.Remove(Master[i1]);
                }
            }
            for (int i2 = 0; (i2 < originalList.Count); i2 = (i2 + 1)) {
                if (Added.Contains(originalList[i2])) {
                    Added.Remove(originalList[i2]);
                }
            }

            System.Collections.Generic.List<PersistentObject> ToBeDeleted = new System.Collections.Generic.List<PersistentObject>();

            for (int i3 = 0; (i3 < Deleted.Count); i3 = (i3 + 1)) {
                if ((originalList.Contains(Deleted[i3]) == false)) {
                    ToBeDeleted.Add(Deleted[i3]);
                }
            }
            for (int i4 = 0; (i4 < ToBeDeleted.Count); i4 = (i4 + 1)) {
                Deleted.Remove(ToBeDeleted[i4]);
            }
        }

        private void deleteItem(PersistentObject item) {
            if (Added.Contains(item)) {
                Added.Remove(item);
            }
            if (originalList.Contains(item)) {
                Deleted.Add(item);
            }
        }

        private void addItem(PersistentObject item) {
            if (Deleted.Contains(item)) {
                Deleted.Remove(item);
            }
            if ((Added.Contains(item) == false)) {
                Added.Add(item);
            }
        }

        private void replaceOldItem(PersistentObject oldItem, PersistentObject newItem) {
            this.deleteItem(oldItem);
            this.addItem(newItem);
        }

        internal void FillData(System.Collections.Generic.IList<PersistentObject> Data) {
            originalList.AddRange(Data);
            Master.AddRange(Data);
        }

        public PersistentObject[] ToArray() {
            return Master.ToArray();
        }
        #endregion

        #region IList<PersistentObject> Members
        public int IndexOf(PersistentObject item) {
            return Master.IndexOf(item);
        }

        public void Insert(int index, PersistentObject item) {
            this.replaceOldItem(this.Master[index], item);
            Master.Insert(index, item);
        }

        public void RemoveAt(int index) {
            this.deleteItem(this.Master[index]);
            Master.RemoveAt(index);
        }

        public virtual PersistentObject this[int index] {
            get {
                return this.Master[index];
            }
            set {
                this.replaceOldItem(this.Master[index], value);
                this.Master[index] = value;
            }
        }
        #endregion

        #region ICollection<PersistentObject> Members
        public void Add(PersistentObject item) {
            if (Master.Contains(item)) {
                return;
            }
            Master.Add(item);
            this.addItem(item);
        }

        public void Clear() {
            for (int i = 0; (i < Master.Count); i = (i + 1)) {
                this.deleteItem(this.Master[i]);
            }
            Master.Clear();
        }

        public bool Contains(PersistentObject item) {
            return Master.Contains(item);
        }

        public void CopyTo(PersistentObject[] array, int arrayIndex) {
            Master.CopyTo(array, arrayIndex);
        }

        public int Count {
            get {
                return Master.Count;
            }
        }

        public bool IsReadOnly {
            get {
                return false;
            }
        }

        public bool Remove(PersistentObject item) {
            if (Master.Contains(item)) {
                this.deleteItem(item);
                Master.Remove(item);
                return true;
            }
            else {
                return false;
            }
        }
        #endregion

        #region #region IEnumerable<PersistentObject> Members
        public System.Collections.Generic.IEnumerator<PersistentObject> GetEnumerator() {
            return Master.GetEnumerator();
        }
        #endregion

        #region #region IEnumerable Members
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
            return Master.GetEnumerator();
        }
        #endregion

        #region #region IListSource Members
        public bool ContainsListCollection {
            get {
                return true;
            }
        }
        public System.Collections.IList GetList() {
            return this.Master;
        }
        #endregion

        #region ICollection Members

        public void CopyTo(System.Array array, int index)
        {
            throw new System.NotImplementedException();
        }

        public bool IsSynchronized
        {
            get { throw new System.NotImplementedException(); }
        }

        public object SyncRoot
        {
            get { throw new System.NotImplementedException(); }
        }

        #endregion
     }
}
